package com.nulldev.util.networking.sctp;

import java.io.IOException;
import java.net.SocketAddress;
import java.nio.ByteBuffer;

import com.nulldev.util.JVM.JVM;
import com.nulldev.util.concurrency.threadIt.v4.asyncTasks.AsyncTaskCallback;
import com.nulldev.util.concurrency.threadIt.v4.emapi.ExecutorManager;
import com.nulldev.util.io.IOUtils;
import com.nulldev.util.networking.ip.IP;
import com.nulldev.util.networking.sctp.Message.MessageAssociation;
import com.sun.nio.sctp.*;

public class SCTPServer implements Runnable {

	public static boolean isSupported() {
		return IP.supportsSCTP();
	}

	private final com.sun.nio.sctp.SctpServerChannel serverChannel;

	private SCTPServer() throws IOException {
		this.serverChannel = com.sun.nio.sctp.SctpServerChannel.open();
	}

	public SCTPServer(final SocketAddress address) throws IOException {
		this();
		this.serverChannel.bind(address);
	}

	public SCTPServer(final SocketAddress address, final int port) throws IOException {
		this();
		this.serverChannel.bind(address, port);
	}

	@Override
	public void run() {
		while (this.serverChannel.isOpen()) {
			try {
				final SctpChannel channel = this.serverChannel.accept();
				final ByteBuffer recvBuf = ByteBuffer.allocateDirect(255);
				ExecutorManager.globalAsyncSubmit(() -> {
					try {

						if (channel == null || !channel.isOpen())
							return true;

						// TODO: Read here
						// TODO: Write here

						final AssociationHandler assocHandler = new AssociationHandler();
						MessageInfo inMessageInfo = null;

						while (true) {
							inMessageInfo = channel.receive(recvBuf, System.out, assocHandler);
							if (inMessageInfo == null || inMessageInfo.bytes() == -1) {
								break;
							}

							final MessageInfo mi = inMessageInfo;
							final Association ass = mi.association();
							final MessageAssociation ma = new MessageAssociation() {

								@Override
								public int id() {
									return ass.associationID();
								}

								@Override
								public int maxInboundStreams() {
									return ass.maxInboundStreams();
								}

								@Override
								public int maxOutboundStreams() {
									return ass.maxOutboundStreams();
								}

							};
							final Message msg = new Message() {

								@Override
								public int stream() {
									return mi.streamNumber();
								}

								@Override
								public int length() {
									return mi.bytes();
								}

								@Override
								public MessageAssociation association() {
									return ma;
								}

								@Override
								public SocketAddress address() {
									return mi.address();
								}
							};

							// msgHandler.onMessage(msg);
						}

						return true;
					} catch (Throwable t) {
						JVM.errorln(t);
						return true;
					}
				}).addCallbackTask(new AsyncTaskCallback<Boolean>() {

					@Override
					public void onCompletion(Boolean result) {
						if (result)
							IOUtils.closeQuietly(channel);
					}
				});
			} catch (Exception e) {
				JVM.errorln(e);
			}
		}
	}
}
